using System;
using System.IO;
using System.Messaging;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Sedna.Core;
using Sedna.Core.Event;

namespace Sedna.Core.Event
{
    /// <summary>
    /// Send event to MSMQ
    /// </summary>
    public class MessageEventSender : IEventSender
    {
        private String queueName;
        private bool createQueue;
        
        public String QueueName
        {
            get { return queueName;}
            set { queueName = value;}
        }
        
        public bool CreateQueue
        {
            get { return createQueue;}
            set { createQueue = value;}
        }
        
        public void Send(IEvent eventObject)
        {
            if (!MessageQueue.Exists(QueueName) && !CreateQueue)
            {
                throw new SednaException(String.Format("Queue {0} doesn't exist", QueueName));
            }
            else if (!MessageQueue.Exists(QueueName))
            {
                MessageQueue.Create(QueueName, true);
            }

            Message message = new Message(eventObject, new EventMessageFormatter());
            MessageQueue queue = new MessageQueue(QueueName);            
            MessageQueueTransaction queueTx = new MessageQueueTransaction();
            try
            {
                queueTx.Begin();
                queue.Send(message, queueTx);
                queueTx.Commit();
            }
            catch (Exception e)
            {
                queueTx.Abort();
                throw new SednaException(e.Message, e);
            }
        }

        #region Message Formatter class

        /// <summary>
        /// Formatter for write message's xml body
        /// </summary>
        private class EventMessageFormatter : IMessageFormatter
        {
            public void Write(Message message, Object eventSource)
            {
                message.BodyStream = new BufferedStream(new MemoryStream());
                new XmlSerializer(eventSource.GetType())
                    .Serialize(message.BodyStream, eventSource);

                if (eventSource is BaseEvent)
                {
                    message.BodyStream.Position = 0;
                    Validate(message.BodyStream, (BaseEvent)eventSource);
                }

                message.BodyStream.Position = 0;
            }

            private void Validate(Stream stream, BaseEvent baseEvent)
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ValidationType = ValidationType.Schema;
                settings.Schemas = baseEvent.ValidateSchemaSet();
                settings.ValidationEventHandler += new ValidationEventHandler(XmlValidationEventHandler);

                XmlReader xmlReader = XmlReader.Create(stream, settings);
                while (xmlReader.Read()) ;
            }

            private void XmlValidationEventHandler(object sender, ValidationEventArgs e)
            {
                throw new SednaException(String.Format("Xml Validation: {0}", e.Message));
            }

            public bool CanRead(Message message)
            {
                return false;
            }

            #region Methods must not be implemented

            public object Read(Message message)
            {
                throw new NotImplementedException();
            }

            public object Clone()
            {
                throw new NotImplementedException();
            }

            #endregion
        }
        
        #endregion
    }
}
